package hu.elte.inf.holiuml.fileutil;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;

import javax.swing.JFileChooser;
import javax.swing.JFrame;

import hu.elte.inf.holiuml.model.Attribute;
import hu.elte.inf.holiuml.model.ClassDiagram;
import hu.elte.inf.holiuml.model.ClassNode;
import hu.elte.inf.holiuml.model.Node;
import hu.elte.inf.holiuml.model.Operation;
import hu.elte.inf.holiuml.model.Parameter;
import hu.elte.inf.holiuml.model.RelationEdge;
import hu.elte.inf.holiuml.model.UMLClass;
import hu.elte.inf.holiuml.model.Var;

/**
 * Java code generator class. @see CodeGenerator
 * 
 * @author Jozsef Sipos
 * 
 */
class JavaCodeGenerator implements CodeGenerator {   

	// TODO JAVADOC!!!!
	// TODO Generate proper code from relations: extends, attributes (Check)

	public void generate(ClassDiagram fromDiagram, JFrame mainFrame)
			throws IOException {
		JFileChooser fileChooser = new JFileChooser();
		fileChooser.setDialogTitle(".java f�jlok l�trehoz�sa");
		fileChooser.setDialogType(JFileChooser.OPEN_DIALOG);
		fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
		fileChooser.setMultiSelectionEnabled(false);

		if (JFileChooser.APPROVE_OPTION == fileChooser
				.showOpenDialog(mainFrame)) {

			File file = fileChooser.getSelectedFile();

			for (ClassNode cn : fromDiagram.getClassNodes()) {
				generate(fromDiagram, cn.getUMLClass(), file);
			}

		} else {
			// Cancel pressed
		}
	}

	/**
	 * 
	 * @param clazz
	 * @param targetDir
	 * @throws FileNotFoundException
	 */
	private void generate(ClassDiagram diagram, UMLClass clazz, File targetDir)
			throws FileNotFoundException {
		StringBuilder sb = new StringBuilder();

		sb.append(String.format("/**\n * %s.java\n *\n * %s\n */\n\n", clazz
				.getName(), clazz.getDescription()));

		sb.append(String.format("public class %s%s {\n\n", clazz.getName(),
				getExtendsClause(diagram, clazz)));
		generateAttributes(clazz, sb);
		sb.append("\n");
		generateRelations(diagram, clazz, sb);
		sb.append("\n\n");
		generateMethods(clazz, sb);
		sb.append("\n\n}");

		PrintWriter pw = null;
		try {
			String targetFileName = targetDir.getPath() + File.separator
					+ clazz.getName() + ".java";
			pw = new PrintWriter(targetFileName);

			pw.print(sb.toString());
		} finally {
			if (pw != null)
				pw.close();
		}
	}

	private String getExtendsClause(ClassDiagram diagram, UMLClass clazz) {

		// Csak egy �soszt�lya lehet, az interf�szeket nem kezeli ez a verzi�
		// m�g
		for (RelationEdge re : diagram.getRelationEdges()) {
			if (re.getRelation().getType().equals("Generalization")) {
				return "extends " + re.getRelation().getRelationEnd().getName();
			}
		}
		return "";
	}

	private void generateRelations(ClassDiagram diagram, UMLClass clazz,
			StringBuilder sb) {
		for (RelationEdge re : diagram.getRelationEdges()) {

			if (re.getRelation().getType().equals("Generalization")) {
				// Ez m�r beleker�lt az extends-be
				continue;
			}

			Node n = re.getNodeBegin();

			if (n instanceof ClassNode && re.getNodeEnd() instanceof ClassNode
					&& ((ClassNode) n).getUMLClass().equals(clazz)) {
				sb.append(String.format("\t// %s\n\t%s %s %s;\n\n", re
						.getRelation().getRelationEnd(), "private",
						((ClassNode) re.getNodeEnd()).getUMLClass().getName(),
						re.getRelation().getRoleBegin()));
			}
		}

	}

	private void generateMethods(UMLClass clazz, StringBuilder sb) {

		for (Operation o : clazz.getOperations()) {

			generateJavaDoc(o, sb);
			sb.append("\n");

			sb.append(String.format("\t%s %s %s(", o.getVisibility(), (o
					.getReturnData() == null ? "void" : o.getReturnData()
					.getType()), o.getName()));

			boolean elsoParam = true;
			for (Var p : o.getParameters()) {
				sb.append(String.format("%s%s %s", (elsoParam ? "" : ", "), p
						.getType(), p.getName()));
				elsoParam = false;
			}

			sb.append(") {\n\t\t//TODO HoliUML autogenerated method stub\n");
			if (o.getReturnData() != null) {
				sb.append(String.format("\t\treturn %s;\n", getReturnValueFor(o
						.getReturnData())));
			}
			sb.append("\t}\n\n");
		}
	}

	private String getReturnValueFor(Var returnData) {
		
		// TODO mivel enum lett a VarType, ez�rt �t lehet �rni most m�r enum �sszehasonl�t�sra
		String type = returnData.getType().toString().toLowerCase();
		if ("int".equals(type) || "byte".equals(type) || "long".equals(type)
				|| "float".equals(type) || "double".equals(type)
				|| "short".equals(type)) {
			return "0";
		} else if ("char".equals(type)) {
			return "'\u0000'";
		} else if ("boolean".equals(type)) {
			return "false";
		} else {
			return "null";
		}
	}

	private void generateJavaDoc(Operation o, StringBuilder sb) {
		sb.append(String.format("\t/**\n\t * %s\n", o.getDescription()));

		for (Var p : o.getParameters()) {
			sb.append(String.format("\t * @param %s %s\n", p.getName(), p
					.getDescription()));
		}
		if (o.getReturnData() != null) {
			sb.append(String.format("\t * @return %s\n", o.getReturnData()
					.getDescription()));
		}
		sb.append("\t */");
	}

	private void generateAttributes(UMLClass clazz, StringBuilder sb) {

		for (Attribute a : clazz.getAttributes()) {
			sb.append(String.format("\t// %s\n\t%s %s %s;\n\n", a
					.getDescription(), a.getVisibility(), a.getType(), a
					.getName()));
		}
	}

}
